//****************************************************************************************
// Name:		Dblib.cpp
// Platform:	SQL Server 2000 SP3a or higher, Windows NT, 2000 or XP
// Author:		Copyright (c) 2006 by Michael Coles, MCDBA
//
// Description:	This library provides DB-LIB functionality to connect to SQL Server from
//				within an extended stored procedure.  DB-LIB is a little more complicated
//				than most methods, but it is much more efficient than most other methods.
//
// LEGAL STUFF:
// ------------
// Copyright (C) 2005 - 2006 by Michael Coles, MCDBA
//
// Some included code included is released under the redistribution agreements as 
// specified by the authors of the respective code.  Copyright holders of this included 
// code maintain all copyright and other rights to their original or derivative works.
//
// All rights reserved.                          
//
// REDISTRIBUTION OF THIS CODE:
// ----------------------------
// All code included in this package is either the original work of the copyright holder,
// or derivative work based on other copyright holders' works.  All derivative works 
// include information as required by the copright holders' redistribution agreements.
// These redistribution agreements, where possible, are included in the text of the source
// code distributed with this code.
//
// Redistribution and use in source and binary forms, with or without modification, are 
// permitted provided that the following conditions are met:
//
//   1. Redistributions of source code must retain the above copyright notice, this list 
//      of conditions and the following disclaimer.
//
//   2. Redistributions in binary form must reproduce the above copyright notice, this 
//      list of conditions and the following disclaimer in the documentation and/or other 
//      materials provided with the distribution.
//
//   3. The names of its contributors may not be used to endorse or promote products 
//      derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT 
// SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 
// WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//****************************************************************************************

#include <dblib.h>
#include <constants.h>

/*DBINT Dblib::currentdb(BYTE *name) {
	strcpy((char *)name, dbname(this->dbproc));
	return XP_NOERROR;
}*/

Dblib::printerror (SRV_PROC *srvproc, char *errormsg)
{
	// Print the error message on the SQL Server output screen
	srv_sendmsg(srvproc,
		SRV_MSG_ERROR,
		1,
		SRV_INFO,
		1,
		NULL,
		0,
		(DBUSMALLINT) __LINE__,
		errormsg,
		SRV_NULLTERM);
	// Alert SQL Server that an error has occurred
	srv_senddone(srvproc,
		(SRV_DONE_ERROR | SRV_DONE_MORE),
		0,
		0);
	return (XP_ERROR);
}

Dblib::printmessage (SRV_PROC *srvproc, char *message)
{
	srv_sendmsg(
		srvproc,
		SRV_MSG_INFO,
		0,
		(DBTINYINT)0,
		(DBTINYINT)0,
		NULL,
		0,
		0,
		message,
		SRV_NULLTERM);
	return (XP_NOERROR);
}

Dblib::err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
{
	SRV_PROC *srvproc = (SRV_PROC *)NULL;
	srvproc = (SRV_PROC *)dbgetuserdata(dbproc);
	srv_sendmsg(srvproc,
		SRV_MSG_ERROR,
		(DBINT)REMOTE_MSG,
		(DBTINYINT)severity,
		(DBTINYINT)0,
		NULL,
		0,
		0,
		dberrstr,
        SRV_NULLTERM);
	if ((dbproc == NULL) || (DBDEAD(dbproc)))
		return(INT_EXIT);
	return(INT_CANCEL);
}

Dblib::msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext)
{
	int rc = XP_NOERROR;
	SRV_PROC *srvproc = (SRV_PROC *)NULL;
	srvproc = (SRV_PROC *)dbgetuserdata(dbproc);
	// Trap login fail message
	if (msgno == REMOTE_FAIL){
		// Send a message to the client that
		// the remote connection failed.
		srv_sendmsg(srvproc,
			SRV_MSG_ERROR,
			(DBINT)msgno,
			(DBTINYINT)severity,
			(DBTINYINT)msgstate,
			NULL,
			0,
			0,
			"Login to remote DBMS failed (dbopen).",
			SRV_NULLTERM);
			rc = XP_ERROR;
	} else if (msgno == 2714) {
		srv_sendmsg(srvproc,
			SRV_MSG_INFO,
			(DBINT)msgno,
			(DBTINYINT)0,
			(DBTINYINT)msgstate,
			NULL,
			0,
			0,
			"Master_Key_Vault table already exists in this database.  This step will be skipped.",
			SRV_NULLTERM);
		rc = XP_NOERROR;
	} else if (severity >= 10) {
		// must be an error message
		srv_sendmsg(srvproc,
			SRV_MSG_ERROR,
			msgno,
			(DBTINYINT)severity,
			(DBTINYINT)msgstate,
			NULL,
			0,
			0,
			(DBCHAR *)msgtext,
			SRV_NULLTERM);
			rc = XP_ERROR;
	}
	return(rc);
}

Dblib::initialize(SRV_PROC *srvproc) {
	this->srvproc = srvproc;
	return (XP_NOERROR);
}

Dblib::connect() {
	//char		sqlstmt[MAX_SQL_LEN];
    RETCODE		rc = XP_NOERROR;		// Value returned from DB-Library calls      
	this->loginrec = dblogin();
	dbprocerrhandle(this->loginrec, (DBERRHANDLE_PROC)Dblib::err_handler);
	dbprocmsghandle(this->loginrec, (DBMSGHANDLE_PROC)Dblib::msg_handler);
	// Check for integrated security
	if (strcmp(srv_pfield(this->srvproc, SRV_LSECURE, (int *)NULL), "TRUE") == 0) {
		// Client has accessed using some form of integrated security
		// Impersonate client and set DBSETLSECURE flag
    	bImpersonated = srv_impersonate_client(this->srvproc);
		DBSETLSECURE(this->loginrec);
	} else {
    	// Client used standard login
    	// Set the user name, password, and application name for the remote
    	DBSETLUSER(this->loginrec, srv_pfield(srvproc, SRV_USER, (int *)NULL));
    	DBSETLPWD(this->loginrec, srv_pfield(srvproc, SRV_PWD, (int *)NULL));
	}
	DBSETLAPP (this->loginrec, APP_NAME);
	// Try to open a connection to the local DBMS.
	// Since the servername parameter is set to NULL, the connection will be
	//	dbproc = dbopen(loginrec, "server_name");
	this->dbproc = dbopen(this->loginrec, NULL);
	dbsetuserdata(this->dbproc, (VOID *)this->srvproc);
	//get the client session token

	//srv_getbindtoken (this->srvproc, this->bindtoken);
	//dbfcmd (this->dbproc, "exec master.dbo.sp_bindsession \'%s\' ", this->bindtoken);
	//dbsqlexec(this->dbproc);
	//while ((rc = dbresults(this->dbproc)) != NO_MORE_RESULTS);
	return (XP_NOERROR);
}

Dblib::getall() {
	RETCODE rc = XP_NOERROR;
	int cols = 0;
	int i = 0;
	int rows = 0;
	// Process the results
	while ((rc = dbresults (this->dbproc)) != NO_MORE_RESULTS)
	{
        // How many data columns are in the row?
		cols = dbnumcols (this->dbproc);
        // Build the row description for the client return.
		for (i = 1; i <= cols; i++) {
			// Call srv_describe for each column that will be sent back via ODS
			//		to the caller of the extended procedure
			srv_describe (this->srvproc, i, (DBCHAR *)dbcolname (dbproc, i), SRV_NULLTERM,
                   (DBINT)dbcoltype (dbproc, i), dbcollen (dbproc, i),
                   (DBINT)dbcoltype (dbproc, i), dbcollen (dbproc, i),
                   (BYTE *)NULL);
		}
		rows = 0;  // Initialize a counter for the row number.
		while (dbnextrow (this->dbproc) != NO_MORE_ROWS){
			// For each data field in the current row, fill the structure that will
			//    be sent back to the caller of the extended procedure
			for (i = 1; i <= cols; i++){
				srv_setcollen (this->srvproc, i, (short)dbdatlen (this->dbproc, i));
		        srv_setcoldata (this->srvproc, i, (void*)dbdata(this->dbproc, i));
			}
			// Send the data row back to SQL Server via ODS
			if (srv_sendrow (this->srvproc) == SUCCEED)
				rows++;                        // Go to the next row. 
		}
	}
	// Send the final done packet for the execution of the command  
	// batch. The previous batch was one that might have returned   
	// rows, so set the DONE status accordingly.                    
	if (rows > 0)
		srv_senddone (this->srvproc, SRV_DONE_COUNT | SRV_DONE_MORE,
              (DBUSMALLINT) 0, rows);
	else 
		srv_senddone (this->srvproc, SRV_DONE_MORE, (DBUSMALLINT) 0, (DBINT) 0);
	return (rc);
}

void Dblib::execute(char *sql)
{
	dbcmd(this->dbproc, sql);
	dbsqlexec(this->dbproc);
	dbresults(this->dbproc);
}

Dblib::disconnect() {
    // Close the connection to SQL Server. 
	dbclose(this->dbproc);
    dbfreelogin(this->loginrec);
	// Revert back to SQL Server's user account
    if( bImpersonated )
	    srv_revert_to_self(this->srvproc);
	return(XP_NOERROR);
}